using System;
using System.Data;
using System.Linq;
using NUnit.Framework;
using PropertyExpression.ReactiveRelation.Test.Common;

namespace PropertyExpression.ReactiveRelation.Test.Demo
{
    [TestFixture]
    public class QuoteTest : BaseDbTest
    {
        [Test]
        public void CalcTotalTest()
        {
            CalcTotal(IsolationLevel.Serializable);
        }

        [Test]
        public void ConcurrentThreadsCalcTotalTest()
        {
            ConcurrentThreadsExecuter.Execute(
                () => CalcTotal(IsolationLevel.ReadCommitted)
                );
        }

        private static void CalcTotal(IsolationLevel isolationLevel)
        {
            Func<Guid> createQuoteFunc =
                () => DataContextProviderHolder.Provider.Execute(
                          context =>
                              {
                                  var quote = CalcExecutorHolder.CalcExecutor.CreateEntity<Quote>();
                                  context.Quotes.InsertOnSubmit(quote);

                                  quote.QuoteId = IdGenerator.GetId();

                                  foreach (var dealerOtherExpense 
                                      in context.Dealers.First().DealerOtherExpenses)
                                  {
                                      var expense = CalcExecutorHolder.CalcExecutor.CreateEntity<QuoteOtherExpense>();
                                      expense.QuoteOtherExpenseId = IdGenerator.GetId();
                                      expense.Description = dealerOtherExpense.Description;
                                      expense.Quantity = 1;
                                      expense.Fix = dealerOtherExpense.Fix;
                                      expense.PartPercent = dealerOtherExpense.PartPercent;
                                      expense.LaborPercent = dealerOtherExpense.LaborPercent;
                                      expense.Quote = quote;
                                  }

                                  var id = quote.QuoteId;

                                  context.SubmitChanges();

                                  return id;
                              }, isolationLevel
                          );

            var quoteId = createQuoteFunc();

            Func<decimal, decimal, Guid> createQuoteJobFunc =
                (jobTime, rate) =>
                DataContextProviderHolder.Provider.Execute(
                    context =>
                        {
                            var quoteJob = CalcExecutorHolder.CalcExecutor.CreateEntity<QuoteJob>();

                            quoteJob.QuoteJobId = IdGenerator.GetId();

                            quoteJob.Quote = context.Quotes.Single(_ => _.QuoteId == quoteId);

                            quoteJob.LaborTime = jobTime;

                            quoteJob.LaborRate = rate;

                            var jobId = quoteJob.QuoteJobId;

                            context.SubmitChanges();

                            return jobId;
                        }, isolationLevel);

            var quoteJobId = createQuoteJobFunc(2.35m, 15);

            Func<int, decimal, Guid> createQuotePartFunc =
                (quantity, price) =>
                DataContextProviderHolder.Provider.Execute(
                    context =>
                        {
                            var quotePart = CalcExecutorHolder.CalcExecutor.CreateEntity<QuotePart>();
                            quotePart.QuotePartId = IdGenerator.GetId();

                            quotePart.QuoteJob = context.QuoteJobs.Single(_ => _.QuoteJobId == quoteJobId);

                            quotePart.Quantity = quantity;
                            quotePart.Price = price;

                            var partId = quotePart.QuotePartId;

                            context.SubmitChanges();

                            return partId;
                        }, isolationLevel);

            var quotePartId = createQuotePartFunc(2, 15.22m);

            Action<Guid, decimal> verifyTotalByQuoteAction =
                (localQuoteId, expected) =>
                DataContextProviderHolder.Provider.Execute(
                    context =>
                    Assert.AreEqual(expected, context.Quotes.Single(_ => _.QuoteId == localQuoteId).QuoteTotal), 
                    isolationLevel);

            Action<decimal> verifyTotalAction =
                expected => verifyTotalByQuoteAction(quoteId, expected);

            verifyTotalAction(453.81m);

            createQuotePartFunc(3, 33.72m);

            verifyTotalAction(579.26m);

            DataContextProviderHolder.Provider.Execute(
                context =>
                    {
                        context.QuoteParts.Single(_ => _.QuotePartId == quotePartId)
                            .Price = 203.44m;

                        context.SubmitChanges();
                    }, isolationLevel);

            verifyTotalAction(1046.04m);

            DataContextProviderHolder.Provider.Execute(
                context =>
                    {
                        var quoteLine = context.QuoteParts.Single(_ => _.QuotePartId == quotePartId);
                        quoteLine.QuoteJob = default(QuoteJob);
                        context.QuoteParts.DeleteOnSubmit(quoteLine);

                        context.SubmitChanges();
                    }, isolationLevel);

            verifyTotalAction(541.51m);

            var quoteJobId1 = quoteJobId;
            quoteJobId = createQuoteJobFunc(758.19m, 124m);

            verifyTotalAction(110069.63m);

            createQuotePartFunc(23, 1.22m);
            createQuotePartFunc(7, 15.22m);

            verifyTotalAction(110236.54m);

            var quoteId2 = createQuoteFunc();

            Action<Guid> setQuoteAction =
                localQuoteId =>
                DataContextProviderHolder.Provider.Execute(
                    context =>
                        {
                            var quoteJob = context.QuoteJobs.Single(job => job.QuoteJobId == quoteJobId1);

                            quoteJob.Quote = context.Quotes.Single(quote => quote.QuoteId == localQuoteId);

                            context.SubmitChanges();
                        }, isolationLevel);

            setQuoteAction(quoteId2);

            verifyTotalAction(110070.05m);
            verifyTotalByQuoteAction(quoteId2, 541.51m);

            setQuoteAction(quoteId);

            verifyTotalAction(110236.54m);
            verifyTotalByQuoteAction(quoteId2, 375m);

            DataContextProviderHolder.Provider.Execute(
                context =>
                    {
                        var otherExpense = (from expense in context.QuoteOtherExpenses
                                            where expense.PartPercent == 18 && expense.QuoteId == quoteId
                                            select expense).Single();
                        otherExpense.Quote = default(Quote);
                        context.QuoteOtherExpenses.DeleteOnSubmit(otherExpense);

                        context.SubmitChanges();
                    }, isolationLevel);

            verifyTotalAction(110194.1m);

            DataContextProviderHolder.Provider.Execute(
                context =>
                    {
                        var otherExpense = CalcExecutorHolder.CalcExecutor.CreateEntity<QuoteOtherExpense>();
                        otherExpense.QuoteOtherExpenseId = IdGenerator.GetId();

                        otherExpense.Quantity = 2;
                        otherExpense.LaborPercent = 43;
                        otherExpense.PartPercent = 77;
                        otherExpense.Fix = 53.2m;

                        otherExpense.Quote = context.Quotes.Single(_ => _.QuoteId == quoteId);

                        context.SubmitChanges();
                    }, isolationLevel);

            verifyTotalAction(191547.28m);

            DataContextProviderHolder.Provider.Execute(
                context =>
                    {
                        (from expense in context.QuoteOtherExpenses
                         where expense.PartPercent == 1 && expense.QuoteId == quoteId
                         select expense).Single().Price = 5575.2m;

                        context.SubmitChanges();
                    }, isolationLevel);

            verifyTotalAction(192367.58m);


            DataContextProviderHolder.Provider.Execute(
                context =>
                    {
                        var quoteJob = context.QuoteJobs.Single(_ => _.QuoteJobId == quoteJobId);

                        foreach (var quotePart in quoteJob.QuoteParts.ToList())
                        {
                            quotePart.QuoteJob = default(QuoteJob);
                            context.QuoteParts.DeleteOnSubmit(quotePart);
                        }

                        quoteJob.Quote = default(Quote);
                        context.QuoteJobs.DeleteOnSubmit(quoteJob);

                        context.SubmitChanges();
                    }, isolationLevel);

            verifyTotalAction(815.8m);
        }

        protected override void DbClean()
        {
            DataContextProviderHolder.Provider
                .Execute(context =>
                             {
                                 context.QuoteJobs.DeleteAll();
                                 context.QuoteOtherExpenses.DeleteAll();
                                 context.Quotes.DeleteAll();
                                 context.QuoteParts.DeleteAll();

                                 context.SubmitChanges();
                             }, 
                         IsolationLevel.Serializable);
        }
    }
}